/*
Copyright (c) [2019年5月1日] [吴超]
[MBT_Studio] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
#include "工程文件.h"
#include <file_文件名数据.h>


#include "A_引擎/引擎.h"
#include "扩展/插件.h"

#include "框架/ui组件构建结构.h"
#include "框架/工作区/节点视口/节点操作.h"
#include "框架/工作区/大纲/大纲工作区.h"
#include "框架/工作区/3D工作区/3D视口工作区.h"
#include "框架/工作区/节点视口/节点视口工作区.h"

#include "物体编辑/物体编辑.h"
#include "物体管理/渲染物体管理.h"
#include "物体管理/渲染物体创建销毁.h"

#include <异步解算/物理异步解算.h>
#include <场景/intern/S_Scene.h>


static S_工程文件* gFile = 0;

static std::vector<C_节点面板*> g_选择节点;
static uint32					g_节点选择数量 = 0;


std::wstring S_节点3D_文件路径::g_案例目录 = L"//照壁/示例/";



typedef struct {
	vec3 pos;
	vec3 rot;
	float32 depth;
	float32 近端;
	float32 远端;
	uint32	投影模式;
}S_摄像机数据文件块;


typedef struct {
	uint32 版本号[2];
	uint64 工程文件类型;
}S_工程文件头;




S_工程文件* f_projectFile_创建() {
	gFile = (S_工程文件*)malloc(sizeof(S_工程文件));
	return gFile;
}

void f_projectFile_销毁(S_工程文件* p) {
	//f_prop_Release(gFile->m_时间区间);
	free(gFile);
}




S_工程文件* f_projectFile_取当前工程文件(bool 创建新文件) {
	if (!gFile) {
		gFile = new S_工程文件;

		f_NodeCtx_set帧区间({ 0, 250 });
		f_NodeCtx_set当前帧(0);
	}
	return gFile;
}






void f_projectFile_加载全局状态(FILE* f) {
	auto 文件块 = f_file_读取文件块(f);

	if (文件块.m_块类型 == E_工程文件块类型::e_工程文件块_全局状态) {
		const uint8* 下一次读取数据指针 = 文件块.m_data;

		if (f_file_版本对比(文件块, S_引擎配置::g_低版本号_9, S_引擎配置::g_文件版本号)) {
			下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, S_节点数据::g_物体全局渲染模式);

			//下一次读取数据指针 = f_prop_Load(S_节点数据状态::g_网格编辑模式, 下一次读取数据指针);
			下一次读取数据指针 = f_prop_Load(S_节点数据状态::g_骨架编辑模式, 下一次读取数据指针);
			下一次读取数据指针 = f_prop_Load(S_节点数据状态::g_灯光编辑模式, 下一次读取数据指针);
			
			下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, f_NodeCtx_get帧().当前帧);
			下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, f_NodeCtx_get帧().帧区间);
			下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, f_NodeCtx_get帧().子帧);


			if (S_节点数据::g_物体全局渲染模式 & E_物体视口显示::e_OVS_预览) {
				S_节点数据::g_物体全局渲染模式 &= E_物体视口显示::e_OVS_预览 ^ 0xffffffff;
			}
			if (S_节点数据::g_物体全局渲染模式 & E_物体视口显示::e_OVS_渲染) {
				S_节点数据::g_物体全局渲染模式 &= E_物体视口显示::e_OVS_渲染 ^ 0xffffffff;
			}
			//S_节点数据::g_物体全局渲染模式 = S_节点数据::g_物体全局渲染模式 & 0x0000ffff;
			S_节点数据::g_物体全局渲染模式 = E_物体渲染模式::e_渲染模式_白模;
		}
		else if (f_file_版本对比(文件块, S_引擎配置::g_低版本号_0, S_引擎配置::g_低版本号_8)) {
			下一次读取数据指针 = f_prop_Load(S_节点数据状态::g_网格编辑模式, 下一次读取数据指针);
			下一次读取数据指针 = f_prop_Load(S_节点数据状态::g_骨架编辑模式, 下一次读取数据指针);
			下一次读取数据指针 = f_prop_Load(S_节点数据状态::g_灯光编辑模式, 下一次读取数据指针);
			下一次读取数据指针 = f_prop_Load(S_节点数据状态::g_3D视口显示模式, 下一次读取数据指针);

			下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, f_NodeCtx_get帧().当前帧);
			下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, f_NodeCtx_get帧().帧区间);
			下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, f_NodeCtx_get帧().子帧);
		}
	}

	f_prop_enum(S_节点数据状态::g_网格编辑模式) = 0;
	f_prop_enum(S_节点数据状态::g_骨架编辑模式) = 0;
	f_prop_enum(S_节点数据状态::g_灯光编辑模式) = 0;
	
	free(文件块.m_data);
}

void f_projectFile_保存全局状态(FILE* f) {
	auto 文件块 = f_文件块_创建(E_工程文件块类型::e_工程文件块_全局状态, S_引擎配置::g_文件版本号);
	S_UI8Array* 块 = (S_UI8Array*)f_core_array_new(0, sizeof(uint8));

	f_文件块_写数据块(块, S_节点数据::g_物体全局渲染模式);

	//f_prop_Save(S_节点数据状态::g_网格编辑模式, 块);
	f_prop_Save(S_节点数据状态::g_骨架编辑模式, 块);
	f_prop_Save(S_节点数据状态::g_灯光编辑模式, 块);
	//f_prop_Save(S_节点数据状态::g_3D视口显示模式, 块);
	f_文件块_写数据块(块, f_NodeCtx_get帧().当前帧);
	f_文件块_写数据块(块, f_NodeCtx_get帧().帧区间);
	f_文件块_写数据块(块, f_NodeCtx_get帧().子帧);
	
	f_file_保存文件块(f, 文件块, 块);
}

void f_projectFile_设置工程目录(std::u16string filename) {
	if (filename.empty()) {
		filename = std::filesystem::temp_directory_path().u16string();
	}
	auto 文件名 = f_file_get无后缀文件名(filename);
	if (文件名 == u".") {
		文件名 = u"";
		time_t t = time(0);
		for (uint32 i = 0; i < 8; ++i) {
			uint32 c = ((t>>i*4)&0xf);
			if(c > 9) c += 9;
			文件名 += 48 + c;
		}
	}

	
	S_引擎配置::g_工程文件名称 = f_str_u16_to_u8(filename);
	S_引擎配置::g_工程文件路径 = f_file_get路径(f_str_string_to_u16(S_引擎配置::g_工程文件名称));
	S_引擎配置::g_资源文件夹 = 文件名 + u"_资源/";
	S_节点数据::g_工程文件节点代码路径 = f_str_u16_to_u8(S_引擎配置::g_工程文件路径 + S_引擎配置::g_资源文件夹);

	if (文件名.empty()) {
		S_框架::m_扩展模块源码路径 = "";
	}
	else {
		S_框架::m_扩展模块源码路径 = S_节点数据::g_工程文件节点代码路径 + "插件/";
	}
	

	f_file_去路径空白字符(S_节点数据::g_工程文件节点代码路径);
	S_节点数据::g_工程文件几何节点代码绝对路径 = S_节点数据::g_工程文件节点代码路径 + "几何/";
	S_节点数据::g_工程文件函数节点代码绝对路径 = S_节点数据::g_工程文件节点代码路径 + "函数/";
	S_节点数据::g_工程文件材质节点代码绝对路径 = S_节点数据::g_工程文件节点代码路径 + "材质/";
	S_节点数据::g_工程文件着色节点代码绝对路径 = S_节点数据::g_工程文件节点代码路径 + "着色/";

	f_file_创建目录(S_节点数据::g_工程文件几何节点代码绝对路径);
	f_file_创建目录(S_节点数据::g_工程文件函数节点代码绝对路径);
	f_file_创建目录(S_节点数据::g_工程文件材质节点代码绝对路径);
	f_file_创建目录(S_节点数据::g_工程文件着色节点代码绝对路径);


}

void f_projectFile_设置文件保存状态(bool 是否已保存) {
	auto 标题 = f_StringToWString(S_引擎配置::g_工程文件名称);
	if (!是否已保存) {
		标题 += L"*";
	}

	for (auto* window : S_框架::g_主窗口) {
		f_window_set窗口标题(window, 标题);
	}
}

void f_projectFile_清除工程数据() {
	S_框架::g_视口相机.clear();
	S_框架::g_拾取物体.clear();

	f_global_clear激活节点();
	
	f_NodeCtx_等待计算完成(3000);
	f_NodeView_清除所有视口中的节点();

	auto* scene = f_NodeCtx_get默认场景();

	f_3D视口工作区_销毁();
	f_大纲视图工作区_销毁();
	f_节点视口工作区_销毁();
	//f_scene_销毁渲染器纹理(scene, 1, E_纹理分组类型::e_2D纹理);
	//f_scene_销毁渲染器纹理(scene, 1, E_纹理分组类型::e_字符纹理);

	f_projectFile_清除焦点更新节点();

	f_world_销毁渲染层(scene->m_默认渲染世界, 1);

	f_node_销毁所有渲染世界();

	f_操作记录_销毁所有记录();
	f_om_销毁所有物体();



	for (auto& e : S_节点数据::g插件库) {
		f_插件_卸载(*e.second);
	}
	S_节点数据::g插件库.clear();
	S_节点数据::g扩展节点库.clear();
	S_框架::g_插件_变换操作.clear();
	S_框架::g_插件后端物体.clear();

	f_projectFile_加载扩展库();
}

void f_projectFile_新建() {
	
	f_projectFile_清除工程数据();

	f_界面_重置工作窗口();
	f_界面_构建工作窗口(true);
}

void f_projectFile_载入(FILE* f) {
	
	f_projectFile_清除工程数据();

	f_界面_重置工作窗口();

	S_文件块 文件块 = f_file_读取文件块(f);
	if (文件块.m_块类型 == E_工程文件块类型::e_工程文件块_文件头) {
		S_引擎配置::g_当前加载文件版本号 = 文件块.m_版本号;

		S_工程文件头 工程文件头 = *((S_工程文件头*)文件块.m_data);
		S_框架::版本号[0] = 工程文件头.版本号[0];
		S_框架::版本号[1] = 工程文件头.版本号[1];

		f_OB_加载模型(f, *S_节点框架::g_3D视口环境);
		
		//f_projectFile_加载全局状态(f);
		f_projectFile_加载渲染世界(f);
		

		S_框架::g_界面构建->f_加载界面框架(f);
		S_框架::g_ui_加载保存信息框->mf_打开关闭(S_框架::g_ui_加载保存信息框, true);
		DEF_记录日志("读取文件块完成");
	}
	
	free(文件块.m_data);
}

void f_projectFile_保存(FILE* f) {
	
	f_file_去路径空白字符(S_节点数据::g_工程文件节点代码路径);
	S_节点数据::g_工程文件几何节点代码绝对路径 = S_节点数据::g_工程文件节点代码路径 + "几何/";
	S_节点数据::g_工程文件函数节点代码绝对路径 = S_节点数据::g_工程文件节点代码路径 + "函数/";
	S_节点数据::g_工程文件材质节点代码绝对路径 = S_节点数据::g_工程文件节点代码路径 + "材质/";
	S_节点数据::g_工程文件着色节点代码绝对路径 = S_节点数据::g_工程文件节点代码路径 + "着色/";

	f_file_创建目录(S_节点数据::g_工程文件几何节点代码绝对路径);
	f_file_创建目录(S_节点数据::g_工程文件函数节点代码绝对路径);
	f_file_创建目录(S_节点数据::g_工程文件材质节点代码绝对路径);
	f_file_创建目录(S_节点数据::g_工程文件着色节点代码绝对路径);
	

	S_工程文件头 工程文件头;
	S_文件块 文件块 = f_文件块_创建(E_工程文件块类型::e_工程文件块_文件头, S_引擎配置::g_文件版本号);
	
	工程文件头.工程文件类型 = f_字符串名称转换("MBT main\0");
	工程文件头.版本号[0] = 1;
	工程文件头.版本号[1] = 1;
	文件块.m_字节数 = sizeof(S_工程文件头);
	文件块.m_data = (uint8*)&工程文件头;
	f_file_保存文件块(f, 文件块, nullptr);

	f_OB_保存模型(f);
	//f_projectFile_保存全局状态(f);
	f_projectFile_保存渲染世界(f);

	S_框架::g_界面构建->f_保存界面框架(f);
}

bool f_projectFile_保存当前文件() {
	auto 扩展名 = f_file_get文件名后缀(S_引擎配置::g_工程文件名称);

	if (扩展名 == ".np") {
		f_file_备份保存文件(S_引擎配置::g_工程文件名称, f_projectFile_保存, S_框架::g_文件备份数量);
		return true;
	}
	return false;
}

void f_projectFile_载入(const std::u16string& path) {
	f_projectFile_新建();

	f_界面_重置工作窗口();

	on_projectFile_文件打开保存(nullptr, {path}, E_对话框类型::e_读取);
}

bool on_projectFile_文件打开保存(C_Widget* self, const std::vector<std::u16string>& filenames, E_对话框类型 方式) {
	C_Widget::g_鼠标样式 = E_鼠标样式::e_鼠标样式_等待;

	switch (方式) {
	case E_对话框类型::e_读取:
		for (auto& 转换路径 : filenames) {
			auto path = f_str_u16_to_u8(转换路径);
			//std::cout << "name == " << path << std::endl;
			f_projectFile_设置工程目录(转换路径);
			FILE* f = fopen(path.c_str(), "rb");
			if (f) {
				auto 扩展名 = f_file_get文件名后缀(path);

				if (扩展名 == ".np") {
					f_projectFile_载入(f);
				}
				else if (扩展名 == ".ms") {
					auto objs = f_load多物体(f, *S_节点框架::g_3D视口环境);
					auto* 材质 = new S_单色面材质(*S_节点框架::g_3D视口环境);
					for (auto& ob : objs) {
						f_ob_set材质(ob, 材质);
						f_NodeCtx_get默认场景()->f_add物体(ob);
					}
				}
				else if (扩展名 == ".ncp") {

				}

				fclose(f);

			}
			else {
				std::cout << "打开文件失败" << std::endl;
			}

		}
		break;
	case E_对话框类型::e_写入:
		for (auto& 转换路径 : filenames) {
			auto 扩展名 = f_file_get文件名后缀(转换路径);
			auto path = f_str_u16_to_u8(转换路径);

			if (扩展名 == u".np") {
				f_projectFile_设置工程目录(转换路径);

				f_file_备份保存文件(path, f_projectFile_保存, S_框架::g_文件备份数量);
			}
			else if (扩展名 == u".ncp") {
				FILE* f = fopen(path.c_str(), "wb");

				if (f) {

					fclose(f);
				}
				else {
					std::cout << "保存文件失败" << std::endl;
				}
			}
			
			
			
		}
		break;
	case E_对话框类型::e_关闭:
		break;
	default:
		break;
	}

	
	//f_ui_switchRootWidget(S_节点框架::g_UIGM, 0);
	f_projectFile_设置文件保存状态(true);


	for (auto& path : filenames) {
		for (auto it = S_框架::g_最近打开文件.begin(); it != S_框架::g_最近打开文件.end(); ) {
			if (*it == path) {
				it = S_框架::g_最近打开文件.erase(it);
				break;
			}
			else {
				++it;
			}
		}
	}

	for (auto& path : filenames) {
		S_框架::g_最近打开文件.insert(S_框架::g_最近打开文件.begin(), path);
	}

	if (S_框架::g_最近打开文件.size()) {
		std::filesystem::path 最近打开文件 = S_引擎配置::g_配置文件路径;
		最近打开文件 += "/MBT3D/最近打开文件.txt";
		f_file_创建目录(最近打开文件.string());
		FILE* f = fopen(最近打开文件.string().c_str(), "wb");
		if (f) {
			f_file_保存字符串组(f, S_框架::g_最近打开文件);
			fclose(f);
		}
	}
	
	

	C_Widget::g_鼠标样式 = E_鼠标样式::e_鼠标样式_默认;
	return true;
}



void f_projectFile_保存节点(C_节点面板** node, uint32 num) {

}

void f_projectFile_保存渲染世界(FILE* f) {
	auto 文件块 = f_文件块_创建(E_节点工程文件块类型::e_工程文件块_渲染世界, S_引擎配置::g_文件版本号);
	S_UI8Array* 块 = (S_UI8Array*)f_core_array_new(0, sizeof(uint8));

	f_文件块_写数据块(块, uint32(S_节点数据::g_渲染世界数据.size()));
	for (auto& e : S_节点数据::g_渲染世界数据) {
		f_文件块_写字符串(块, e.first);

		f_文件块_写数据块(块, uint32(e.second->m_子物体.size()));
		for (auto& 层 : e.second->m_子物体) {
			S_渲染层* 渲染层 = (S_渲染层*)层->m_UserData;

			f_文件块_写字符串(块, 层->m_Name);

			f_文件块_写数据块(块, uint32(层->m_子物体.size()));
			for (auto& ob : 层->m_子物体) {
				S_渲染对象& 渲染对象 = *(S_渲染对象*)ob;
				f_文件块_写字符串(块, ob->m_Name);
				f_文件块_写数据块(块, 渲染对象.m_场景遮罩);
				f_文件块_写数据块(块, 渲染对象.m_物体遮罩);
				f_文件块_写数据块(块, 渲染对象.m_物体光遮罩);
			}
		}
	}

	f_file_保存文件块(f, 文件块, 块);
}

void f_projectFile_加载渲染世界(FILE* f) {
	S_文件块 文件块 = f_file_读取文件块(f);

	if (文件块.m_块类型 == E_节点工程文件块类型::e_工程文件块_渲染世界) {
		const uint8* 下一次读取数据指针 = 文件块.m_data;

		if (f_file_版本对比(文件块, S_引擎配置::g_低版本号_21, S_引擎配置::g_文件版本号)) {

			uint32 渲染世界数量 = 0;
			下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, 渲染世界数量);
			for (uint32 i = 0; i < 渲染世界数量; ++i) {
				std::u16string 渲染世界名称;
				下一次读取数据指针 = f_文件块_读字符串(下一次读取数据指针, 渲染世界名称);
				auto* 渲染世界 = f_node_创建渲染世界(渲染世界名称, *S_节点框架::g_3D视口环境);


				uint32 渲染层数量 = 0;
				下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, 渲染层数量);
				for (uint32 k = 0; k < 渲染层数量; ++k) {
					std::u16string 层名称;
					uint8 mask = 0;
					下一次读取数据指针 = f_文件块_读字符串(下一次读取数据指针, 层名称);

					S_Object* 层 = f_node_创建渲染层(渲染世界, 层名称, *S_节点框架::g_3D视口环境);
					S_渲染层* 渲染层 = (S_渲染层*)层->m_UserData;
					

					std::vector<S_渲染对象*> 链接物体;

					uint32 物体数量 = 0;
					下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, 物体数量);
					for (uint32 j = 0; j < 物体数量; ++j) {
						S_渲染对象* 渲染对象 = new S_渲染对象();
						std::u16string 物体名称;
						下一次读取数据指针 = f_文件块_读字符串(下一次读取数据指针, 物体名称);
						下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, 渲染对象->m_场景遮罩);
						下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, 渲染对象->m_物体遮罩);
						下一次读取数据指针 = f_文件块_读数据块(下一次读取数据指针, 渲染对象->m_物体光遮罩);
						
						if (f_global_渲染物体().find(物体名称) != f_global_渲染物体().end()) {
							渲染对象->m_物体 = (S_物体*)f_global_渲染物体()[物体名称];
							渲染对象->m_Name = 物体名称;

							链接物体.emplace_back(渲染对象);
						}
					}
					f_OE_物体添加到渲染层(层, 链接物体);
				}
			}
		}
	}

	free(文件块.m_data);
}



Inline S_插件* f_创建插件(const std::u16string& name) {
	if (S_节点数据::g插件库.find(name) == S_节点数据::g插件库.end()) {
		auto* 插件 = new S_插件;
		插件->m_Name = name;
		S_节点数据::g插件库[name] = 插件;

		return 插件;
	}
	return nullptr;
}

void f_projectFile_加载扩展库() {
	uint32 num;
	S_目录文件包 文件包;
	std::vector<std::string> 扩展名过滤 = { ".dll", ".bc" };


	std::string 目录路径;
#ifdef _DEBUG
	目录路径 = S_框架::m_库默认路径.string() + "插件模块/Debug/";
#else
	目录路径 = S_框架::m_库默认路径.string() + "插件模块/";
#endif // DEBUG

	

	f_file_get目录下所有文件名(目录路径, 文件包, 扩展名过滤);
	num = 文件包.文件.size();
	for (uint32 i = 0; i < num; ++i) {
		const auto& file = 目录路径 / std::filesystem::path(文件包.文件[i]);
		auto name = f_file_get无后缀文件名(file.u16string());

		auto* 插件 = f_创建插件(name);

		if (插件 && !插件->m_库) {
			f_插件_加载(*插件);
		}
	}



	文件包.文件.clear();
	文件包.目录.clear();

	f_file_get目录下所有目录名(S_框架::m_扩展模块源码路径.string(), 文件包);

	num = 文件包.目录.size();
	for (uint32 i = 0; i < num; ++i) {
		auto& file = 文件包.目录[i];

		if (S_节点数据::g库名称分配器.f_已存在(file) == false) {
			f_创建插件(file);

			S_节点数据::g插件库[file]->m_链接源码 = true;
		}
	}
}













